{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 右值引用与`std::move`\n",
    "\n",
    "**右值引用**（rvalue reference）是 C++11 的新语言特性，某些场景使用 `rvalue ref` 有更高的性能；右值可以简单理解为”即将作废的对象”。\n",
    "\n",
    "举个例子："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "#include <string>\n",
    "#include <iostream>\n",
    "\n",
    "int main() {\n",
    "  std::string tmp(\"bert\");\n",
    "  std::string name(tmp);\n",
    "  \n",
    "  std::cout << \"tmp: \" << tmp << \"\\n\";\n",
    "  std::cout << \"name: \" << name << \"\\n\";\n",
    "\n",
    "  return 0;\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这是 cpp11 之前，拷贝字符串的一个简单例子；\n",
    "- 第5行声明了一个栈上的对象 `tmp`，它用`\"bert\"`字符串进行初始化；\n",
    "- 第6行声明了一个对象 `name`，使用参数 `tmp` 调用 `string` 的拷贝构造函数\n",
    "\n",
    "因此对象 `name` 复制了`\"bert\"`的一个副本，如图所示：\n",
    "\n",
    "![01](./images/01.png)\n",
    "\n",
    "\n",
    "> 是复制, 还是共同指向一个副本?\n",
    "\n",
    "https://pythontutor.com/cpp.html#mode=edit\n",
    "\n",
    "![02](./images/02.png)\n",
    "\n",
    "https://godbolt.org/z/W9rEaeWqr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "假设上面的例子中，当把 `tmp` 对象赋值给 `name` 之后，`tmp` 就不会再使用了；\n",
    "观察上图，我们可以看到，拷贝`\"bert\"`字符串似乎是个浪费。\n",
    "\n",
    "所以在 cpp11 中引入了右值引用，解决这一问题："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "#include <string>\n",
    "#include <iostream>\n",
    "\n",
    "int main() {\n",
    "  std::string tmp(\"bert\"); // 现在，tmp已经处于一种无效状态，你不应该使用tmp了 ?\n",
    "  std::string name(std::move(tmp));\n",
    "  \n",
    "  std::cout << \"tmp: \" << tmp << \"\\n\";\n",
    "  std::cout << \"name: \" << name << \"\\n\";\n",
    "\n",
    "  return 0;\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意两次代码之间的区别：在 `tmp` 前我们添加了 `std::move`，顾名思义，意思就是将 `tmp` 的内容移动到 `name` 中去，效果如图：\n",
    "\n",
    "![03](./images/03.png)\n",
    "\n",
    "可以看到，`move` 操作将原本属于 `tmp` 的字符串`\"bert\"`，转移给了 `name` 对象；现在，`tmp` 对象内部的字符串指针指向了 `NULL`，所以 `tmp` 不应该再被访问使用了。\n",
    "\n",
    "![04](./images/04.png)\n",
    "\n",
    "使用`move`操作，这里几乎没有额外的开销，只需要将内部指针重新赋值即可。比起原始的深拷贝操作，提升了效率。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意：并不是任何场合都要用 `move`。现代编译器足够的聪明，特别是能够执行**RVO(Return Value Optimization)**优化的地方，不要加 `std::move`。\n",
    "\n",
    "有兴趣的可以自行搜索返回值优化，Effective C++书籍也有讲解，举个例子:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "#include <iostream>\n",
    "\n",
    "// 代码示例\n",
    "std::string MakeName(const std::string& prefix) {\n",
    "    std::string name(prefix);\n",
    "    name += \".txt\";\n",
    "\n",
    "    return name; // RVO优化\n",
    "\n",
    "    //return std::move(name); 如果你画蛇添足使用了这行代码，反而防止了编译器优化，带来一次move constructor的开销\n",
    "}\n",
    "\n",
    "\n",
    "std::string n = MakeName(\"hello\");\n",
    "// 由于RVO优化，函数内局部变量name直接在n上构建。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "move vs rvo: 通过对比`return std::move(name);`与`return name;`的汇编代码\n",
    "\n",
    "```shell\n",
    "gcc -fomit-frame-pointer -fverbose-asm -S move_move.cpp -o move_move.S\n",
    "```\n",
    "```shell\n",
    "gcc -fomit-frame-pointer -fverbose-asm -S move_rvo.cpp -o move_rvo.S\n",
    "```\n",
    "\n",
    "![05](./images/05.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 智能指针\n",
    "\n",
    "C/C++ 是少有的不带 gc(垃圾回收)的现代语言，程序员可以自由的操作内存，但这也是最为人诟病的地方。内存泄漏、野指针、多次释放等错误想必是许多 C/C++ 程序员的噩梦。\n",
    "\n",
    "智能指针的出现，使得 C++ 成为一种“自带 gc”的语言。笔者认为只要掌握好三种智能指针，并合理运用 STL 容器替代数组，想出内存问题都难。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## unique_ptr\n",
    "\n",
    "首先介绍最简单也是最常用的智能指针，叫做 `unique_ptr`，它表示对资源的一种独占，运用 C++ **RAII** 机制，实现了作用域范围的内存控制。 \n",
    "\n",
    "下面看下这块不使用智能指针的代码：\n",
    "\n",
    "[no_unique_ptr.cpp](./no_unique_ptr.cpp)\n",
    "\n",
    "新建文件no_unique_ptr.cpp完成代码编写后，运行命令进行编译。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "#include <iostream>\n",
    "#include <memory>\n",
    "\n",
    "class Student {\n",
    "public:\n",
    "  Student() { std::cout << \"Construct student\" << std::endl; }\n",
    "  ~Student() { std::cout << \"Destruct student\" << std::endl; }\n",
    "\n",
    "  bool Register() { return true; }\n",
    "  bool Enroll() { return true; }\n",
    "};"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "int main() {\n",
    "  Student *p = new Student();\n",
    "  if (!p->Register()) {\n",
    "    delete p;\n",
    "    return -1;\n",
    "  }\n",
    "\n",
    "  if (!p->Enroll()) {\n",
    "    delete p;\n",
    "    return -2;\n",
    "  }\n",
    "\n",
    "  delete p;\n",
    "  return 0;\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "g++命令构建程序"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "!g++ -std=c++11 -o no_unique_ptr no_unique_ptr.cpp;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "!./no_unique_ptr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "该代码使用`new`在堆上分配一个 `Student` 对象，并对其调用了两个成员函数：`Register` 和 `Enroll`。\n",
    "\n",
    "任意一个失败都将返回失败，并且不能忘记 `delete` 资源。在三处 `return` 语句之前，必须加上 `delete p`;\n",
    "\n",
    "否则就可能造成资源泄漏；如果使用 `unique_ptr`，我们就可以在指针 `p` 退出作用域时，自动释放所分配的资源，完整代码如下：\n",
    "\n",
    "[unique_ptr_test.cpp](./unique_ptr_test.cpp)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "#include <iostream>\n",
    "#include <memory>\n",
    "\n",
    "class Student {\n",
    "public:\n",
    "  Student() { std::cout << \"Construct student\" << std::endl; }\n",
    "  ~Student() { std::cout << \"Destruct student\" << std::endl; }\n",
    "\n",
    "  bool Register() { return true; }\n",
    "  bool Enroll() { return true; }\n",
    "};"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "int main() {\n",
    "  std::unique_ptr<Student> p(new Student());\n",
    "  if (!p->Register()) {\n",
    "    // 不需要任何操作，p指向的资源会自动释放\n",
    "    return -1;\n",
    "  }\n",
    "\n",
    "  if (!p->Enroll()) {\n",
    "    // 不需要任何操作，p指向的资源会自动释放\n",
    "    return -2;\n",
    "  }\n",
    "\n",
    "  // 不需要任何操作，p指向的资源会自动释放\n",
    "  return 0;\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "g++命令构建程序"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "!g++ -std=c++11 -o unique_ptr_test unique_ptr_test.cpp;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "!./unique_ptr_test"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "利用 `unique_ptr`，无须操心释放资源，避免内存泄漏。\n",
    "\n",
    "另外，`unique_ptr`也适用于消除“内部分配，外部释放”这种易错的机制，比如C语言的`strdup`函数：\n",
    "\n",
    "[strdup.cpp](./strdup.cpp)\n",
    "\n",
    "可以看到`strdup`函数的使用，给使用者打开了犯错的窗户，一不小心就会内存泄漏。而使用 `unique_ptr`则可以解决这一问题：\n",
    "> OpenAI: `strdup`是一个C/C++的库函数，其作用是将字符串拷贝一份，返回值是指向该字符串的指针。该函数会动态地分配内存空间，以容纳当前字符串，并将其作为新字符串返回。这在需要创建字符串副本时非常方便，特别是当原字符串长度未知时.\n",
    "\n",
    "[unique_ptr_strdup.cpp](./unique_ptr_strdup.cpp)\n",
    "\n",
    "通过返回 `unique_ptr` 替代裸指针，无须使用者操心内存的释放问题，非常安全。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "#include <cstring>\n",
    "#include <memory>\n",
    "\n",
    "std::unique_ptr<char[]> my_strdup(const char *s) {\n",
    "  if (s == nullptr)\n",
    "    return std::unique_ptr<char[]>(nullptr);\n",
    "\n",
    "  // 计算字符串长度\n",
    "  size_t len = strlen(s);\n",
    "\n",
    "  // 用智能指针管理分配的内存\n",
    "  std::unique_ptr<char[]> str(new char[len + 1]);\n",
    "\n",
    "  // 拷贝字符串s\n",
    "  strcpy(str.get(), s);\n",
    "\n",
    "  // 返回分配的新字符串\n",
    "  return str;\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "auto p = my_strdup(\"bert\");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### unique_ptr 的使用注意\n",
    "\n",
    "`unique_ptr`是旧标准库中`auto_ptr`的升级版，由于独占性语义，它不允许被复制，但是可以`move`：\n",
    "\n",
    "[unique_copy_move.cpp](./unique_copy_move.cpp)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "#include <iostream>\n",
    "#include <memory>\n",
    "\n",
    "class Student {\n",
    "   public:\n",
    "    Student() { std::cout << \"Construct student\" << std::endl; }\n",
    "    ~Student() { std::cout << \"Destruct student\" << std::endl; }\n",
    "\n",
    "    bool Register() { return true; }\n",
    "    bool Enroll() { return true; }\n",
    "};"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "int main() {\n",
    "    std::unique_ptr<Student> p(new Student());\n",
    "    std::unique_ptr<Student> p2(std::move(p));\n",
    "\n",
    "    if (p.get() == nullptr) {\n",
    "        std::cout << \"p is empty (nullptr).\" << std::endl;\n",
    "    } else {\n",
    "        std::cout << \"p is not empty.\" << std::endl;\n",
    "    }\n",
    "\n",
    "    return 0;\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "所以，`unique_ptr`是可以被放到 STL 容器中；旧版本要求容器中的元素必须可以被复制，而现在放宽了：`movable`的对象也是可以放入容器的："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "std::vector<std::unique_ptr<int>> ptr_vec;\n",
    "ptr_vec.push_back(std::unique_ptr<int>(new int(123)));\n",
    "*ptr_vec[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## shared_ptr\n",
    "\n",
    "`shared_ptr` 是功能非常强大的智能指针，和 `unique_ptr` 不同的是，它是基于引用计数的，可以被多个所有者共同持有；当最后一个持有者退出作用域时，资源自动释放；它完美解决了资源释放时机的问题，试想一下多个裸指针指向同一个资源时，释放资源将是多么头疼和难以正确，甚至需要使用观察者模式来帮助使用者清理指针。\n",
    "\n",
    "而 `shared-ptr` 完美解决了这一问题，完整代码如下：\n",
    "\n",
    "[shared_ptr_test.cpp](./shared_ptr_test.cpp)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "#include <iostream>\n",
    "#include <memory>\n",
    "#include <thread>\n",
    "\n",
    "// typedef int Resourse; // 报错\n",
    "// using Resourse = int; // 报错\n",
    "\n",
    "std::shared_ptr<int> CreateResourse() { \n",
    "  return std::make_shared<int>(1); \n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "void User1(std::shared_ptr<int> p) {\n",
    "  if (p) {\n",
    "    // 1 使用p\n",
    "    std::cout << \"use p in user1 thread.\" << \"\\n\";\n",
    "  }\n",
    "\n",
    "  std::cout << \"User1: use count = \" << p.use_count() << \"\\n\";\n",
    "\n",
    "  return;\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "void User2(std::shared_ptr<int> p) {\n",
    "  std::this_thread::sleep_for(std::chrono::milliseconds(10));\n",
    "\n",
    "  if (p) {\n",
    "    // 2 使用p\n",
    "    std::cout << \"use p in user2 thread.\" << \"\\n\";\n",
    "  }\n",
    "\n",
    "  std::cout << \"User2: use count = \" << p.use_count() << \"\\n\";\n",
    "\n",
    "  return;\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "int main() {\n",
    "  std::shared_ptr<int> res = CreateResourse();\n",
    "\n",
    "  std::cout << \"CreateResourse: use count = \" << res.use_count() << \"\\n\";\n",
    "\n",
    "  // 启动线程t1和t2，运行user函数，使用res\n",
    "  std::thread t1(User1, res);\n",
    "  std::thread t2(User2, res);\n",
    "\n",
    "  // 等待线程结束，谁也不需要考虑res资源的释放\n",
    "  std::cout << \"thread t2: use count = \" << res.use_count() << \"\\n\";\n",
    "  t1.join();\n",
    "  std::cout << \"t1.join(): use count = \" << res.use_count() << \"\\n\";\n",
    "  t2.join();\n",
    "  std::cout << \"t2.join(): use count = \" << res.use_count() << \"\\n\";\n",
    "\n",
    "  return 0;\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "g++命令构建程序"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "!g++ -o shared_ptr_test -std=c++11 shared_ptr_test.cpp -lpthread"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "!./shared_ptr_test"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "另外，如果你有其它语言的基础，比如 java，可以把 `shared_ptr` 理解为 java 中的引用：多个引用指向一个对象，只有最后一个引用失效时候，该对象才可以被垃圾回收。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## weak_ptr\n",
    "\n",
    "weak_ptr 是配合 `shared_ptr` 存在，主要是为了解决两个问题：\n",
    "1. **循环引用问题**，使得资源无法释放；例如 `A` 对象含有一个 `shared_ptr<B>`，而 `B` 对象也含有一个 `shared_ptr<A>`，那么很容易产生循环引用，使得内存无法释放。本文不详细阐述这个问题，有兴趣可以参考《C++标准库第 2 版》的 5.2 节\n",
    "2. `weak_ptr` 要解决的另外一个问题是臭名昭著的**悬挂指针**(dangling pointer)：指针指向的内存被删除；一个简单的场景是，`A` 线程创建资源，并传递给 `B` 线程，`B` 线程只读访问资源；但是 `A` 线程随后可能释放了资源，`B` 没有感知，而得到了一个悬挂指针\n",
    "\n",
    "下面我们看一下 `weak_ptr` 如何解决这个问题；细节请参考代码和注释：\n",
    "\n",
    "[weak_ptr_test.cpp](weak_ptr_test.cpp)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "#include <iostream>\n",
    "#include <memory>\n",
    "#include <thread>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "std::shared_ptr<int> g_resourse;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "void thread_a() {\n",
    "  // 2. 创建全局资源\n",
    "  g_resourse = std::make_shared<int>(1);\n",
    "\n",
    "  // 3. 睡眠3秒钟\n",
    "  std::this_thread::sleep_for(std::chrono::seconds(3));\n",
    "\n",
    "  // 6. 释放资源\n",
    "  g_resourse = nullptr;\n",
    "\n",
    "  std::cout << \"free resourse, thread A exit.\" << std::endl;\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "void thread_b() {\n",
    "  //  1. 休眠，让线程A先创建资源\n",
    "  std::this_thread::sleep_for(std::chrono::milliseconds(100));\n",
    "\n",
    "  // 4. 创建weak_ptr访问资源,它可以有效检测出悬挂指针：\n",
    "  std::weak_ptr<int> pw(g_resourse);\n",
    "\n",
    "  // 5. 隔一秒钟访问资源，若资源被释放了，则退出线程；\n",
    "  int i = 0;\n",
    "  while (1) {\n",
    "    i++;\n",
    "\n",
    "    // 调用weak_ptr的lock()尝试提升到shared_ptr\n",
    "    auto res(pw.lock());\n",
    "\n",
    "    if (res) {\n",
    "      // 在6之前: 提升成功，指针res有效，可以使用资源，然后睡眠1秒钟\n",
    "      std::cout << i << \":Success read resourse from thread B.\" << std::endl;\n",
    "      std::this_thread::sleep_for(std::chrono::seconds(1));\n",
    "    } else {\n",
    "      std::cout << \"Fail read resourse from thread B, exit.\" << std::endl;\n",
    "      return; // 7. 说明资源被释放了，出现了\"悬挂指针\"情况，线程退出\n",
    "    }\n",
    "  }\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "int main() {\n",
    "  // 启动线程A\n",
    "  std::thread t_a(thread_a);\n",
    "  // 启动线程B\n",
    "  std::thread t_b(thread_b);\n",
    "  // 请注意看线程代码注释中的序号，大致代表了代码的执行顺序\n",
    "\n",
    "  // 等待线程结束\n",
    "  t_a.join();\n",
    "  t_b.join();\n",
    "  \n",
    "  return 0;\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "g++命令构建程序"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "!g++ -o weak_ptr_test -std=c++11 weak_ptr_test.cpp -lpthread"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "!./weak_ptr_test"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意最后两行，首先 `A` 线程释放了资源并退出，然后 `B` 线程使用 `weak_ptr` 感知到了资源释放，避免了出现悬挂指针错误"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# lambda, bind, function\n",
    "\n",
    "C++11 引入了 `lambda`，它极大的方便了程序员编写临时函数，甚至可以模拟闭包。而 bind 可以适配函数签名，极其灵活。\n",
    "\n",
    "下面这个例子展示了如何使用 `bind` 将签名不匹配的 `lambda` 和 `function` 适配起来,完整代码如下：\n",
    "\n",
    "[lambda_test.cpp](./lambda_test.cpp)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "#include <functional>\n",
    "#include <iostream>\n",
    "#include <memory>\n",
    "\n",
    "int main() {\n",
    "  int r = 0;\n",
    "\n",
    "  // lambda，是一个可执行对象，类型是void (int* )\n",
    "  auto updateLambda = [](int *res) { (*res)++; };\n",
    "  // 将lambda赋值给callback，后者类型是 void (), 由于签名不符，需要bind做适配\n",
    "  std::function<void()> callback = std::bind(updateLambda, &r);\n",
    "\n",
    "  // 执行callback\n",
    "  callback();\n",
    "\n",
    "  // 现在，r应该递增为1\n",
    "  std::cout << \"callback From main: r = \" << r << std::endl;\n",
    "\n",
    "  updateLambda(&r);\n",
    "\n",
    "  std::cout << \"updateLambda From main: r = \" << r << std::endl;\n",
    "\n",
    "  return 0;\n",
    "}\n",
    "\n",
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意使用 `bind` 将 `void(int*)`类型的 lambda，适配到类型为 `void()`的 function ; `Lambda` 和 `bind` 在实际运用中较为灵活，项目中有使用到的地方到时还会继续讲解。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "不使用`bind`, 直接使用`lambda`赋值给`function`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "#include <functional>\n",
    "#include <iostream>\n",
    "#include <memory>\n",
    "\n",
    "int main() {\n",
    "  int r = 0;\n",
    "\n",
    "  // 将lambda表达式赋值给callback\n",
    "  std::function<void()> callback = [&r]() { r++; };\n",
    "\n",
    "  // 执行callback\n",
    "  callback();\n",
    "\n",
    "  // 现在，r应该递增为1\n",
    "  std::cout << \"callback From main: r = \" << r << std::endl;\n",
    "\n",
    "  auto updateLambda = [&r]() { r++; };\n",
    "\n",
    "  updateLambda();\n",
    "\n",
    "  std::cout << \"updateLambda From main: r = \" << r << std::endl;\n",
    "\n",
    "  return 0;\n",
    "}\n",
    "\n",
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "g++命令构建程序"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "!g++ -o lambda_test -std=c++11 lambda_test.cpp"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "!./lambda_test"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 语法糖：auto，foreach\n",
    "\n",
    "C++11 加入了一些语法糖，使得写代码更为便利。\n",
    "例如以前声明一个迭代器："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "#include <iostream>\n",
    "#include <map>\n",
    "\n",
    "std::map<int, int> mm;\n",
    "std::map<int, int>::const_iterator it(mm.begin());"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在只需要："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "auto it(mm.begin());"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "以前遍历 `map`："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "for (std::map<int, int>::const_iterator it(mm.cbegin()); it != mm.cend(); ++it) {\n",
    "  std::cout << it->first << \" -> \" << it->second << std::endl;\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在使用 `foreach` 遍历："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{ 1 => 1, 2 => 2, 3 => 3 }"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "std::map<int, int> mm {{1,1}, {2,2}, {3,3}};\n",
    "mm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1->1\n",
      "2->2\n",
      "3->3\n"
     ]
    }
   ],
   "source": [
    "for (const auto &kv : mm) {\n",
    "  std::cout << kv.first << \"->\" << kv.second << std::endl;\n",
    "}"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "C++17",
   "language": "C++17",
   "name": "xcpp17"
  },
  "language_info": {
   "codemirror_mode": "text/x-c++src",
   "file_extension": ".cpp",
   "mimetype": "text/x-c++src",
   "name": "c++",
   "version": "17"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
